package org.bioversityinternational.mgbms.model.dao.base;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.Random;
import java.util.ResourceBundle;

import javassist.NotFoundException;

import org.apache.log4j.Logger;

import org.hibernate.Query;

import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

public abstract class BaseDao<T, PK extends Serializable> extends org.bioversityinternational.db.genericdao.impl.GenericDaoHibernateImpl<T, PK>
{
	private static final Logger LOG = Logger.getLogger(BaseDao.class);
	
	private static ResourceBundle bundle = ResourceBundle.getBundle(BaseDao.class.getName());
	
	public BaseDao(Class<T> type) {
		super(type);
	}
	
	abstract public PK getIdFromString(String sIdAsString);
	
	@Transactional(propagation=Propagation.SUPPORTS)
	public Map<String, Serializable> getFieldsById(String id, List<String> fieldNames) throws ClassNotFoundException
	{
		String sQuery = "select ";
		for (String fn : fieldNames)
		{
			String[] splittedFieldName = fn.split("\\.");
			Class actualClassContainingField = splittedFieldName.length < 2 ? type : Class.forName(type.getPackage().getName() + "." + splittedFieldName[splittedFieldName.length - 2].substring(0, 1).toUpperCase() + splittedFieldName[splittedFieldName.length - 2].substring(1));
			List<String> forbiddenPropertiesForThisClass = getForbiddenPropertiesForClass(actualClassContainingField);
			if (forbiddenPropertiesForThisClass != null && forbiddenPropertiesForThisClass.contains(splittedFieldName[splittedFieldName.length - 1]))
				throw new SecurityException("Not allowed to retrieve " + fn + " from " + type.getSimpleName());

			sQuery += (sQuery.equals("select ") ? "" : ",") + fn;
		}
		sQuery += " from " + type.getName() + " where id=?";
		Query query = getSession().createQuery(sQuery);
		query.setSerializable(0, getIdFromString(id));
		
		Map<String, Serializable> result = new LinkedHashMap<String, Serializable>();
		Object[] fieldValues = (Object[]) query.uniqueResult();
		if (fieldValues == null)
			return null;
		
		for (Object fieldValue : fieldValues)
			if (fieldValue != null)
				result.put(fieldNames.get(result.size()), (Serializable) fieldValue);
		
        LOG.debug("hql: " + sQuery + " -> " + result.size() + " results");
		return result;
	}
	
    @SuppressWarnings("unchecked")
	@Transactional(propagation=Propagation.SUPPORTS)
    public T read(String id)
    {
        return (T) getSession().get(type, getIdFromString(id));
    }

	public static List<String> getForbiddenPropertiesForClass(Class domainClass)
	{
		try
		{
			return Arrays.asList(bundle.getString("forbiddenPropertiesForDomainClass." + domainClass.getSimpleName()).split(","));
		}
		catch (MissingResourceException mre)
		{
			return new ArrayList<String>();
		}
	}
	
	static public String generateRandomPassword(int length)
	{
		Random rgen = new Random();
		byte decision, numValue;
		char charValue;
		StringBuilder password = new StringBuilder();
		while(password.length() < length){
			decision = (byte)rgen.nextInt(2);
			numValue = (byte)rgen.nextInt(10);
			charValue = (char)(rgen.nextInt(25) + 65);
			password.append( (decision%2 == 0) ? ( charValue + "" ) : ( numValue + "") );
		}
		return password.toString();
	}
}

